<!DOCTYPE html>
<uieip type="page" value="login" debug="true" timeout="5000"></uieip>
<uieip type="reg" value="console,model" />
<html>

<head>
    <uieip type="file" value="inc" src="/yqwin/easyui/include.txt" cache="true" minute="1440"></uieip>
    <uieip type="file" value="css" src="/yqwin/css/yqwin.min"></uieip>
    <uieip type="file" value="js" src="/yqwin/src/yqwin.min"></uieip>
    <script src="/adp/stock/js/pub.js"></script>
    <uieip type="tmpl" value="model.title"></uieip>
</head>

<body style="margin: 0;">
    <script type="text/javascript">
        var m_ = "1234567890";

        function a() {
            var mm1111 = '';
            mm1111 = '我的数字 hello';
        }

        var _tb123 = { uitype: "datagrid", dbname: "*" };
    </script>
    <table id="tb_stock">
    </table>
    <script>
        <uieip type="tmpl" value="model.params"></uieip>

        $(function () {
            main_fun.init();
        });

        var page_fun = {
            init: function () {
                $(window).resize(function () {
                    page_fun.onresize();
                });
                page_fun.onresize();
                main_fun.load_data();
            },
            onresize: function () {
                main_fun.resize({
                    width: $(window).width(),
                    height: $(window).height()
                });
            }
        }

        var main_fun = {
            id: "tb_stock",
            init: function () {
                var opt = $.extend(_stock, {
                    ajaxUrl: "/api/stock/stock_list.json",
                    loadData: false,
                    linkField: ["ts_code"],
                    btnMini: false,
                    editLinks: [
                        {
                            id: "load_svr_data",
                            text: "重新下载股票列表",
                            iconCls: "yqwin-btn-icon fa fa-cloud-download",
                            onClick: function () {
                                main_fun.load_svr_data();
                            }
                        }, {
                            id: "txt_ent_date",
                            type: "textbox",
                            width: 177,
                            label: "行情截至日期：",
                            labelWidth: 104,
                            labelAlign: "right",
                            value: "",
                            readonly: true
                        }, {
                            id: "load_svr_daily",
                            text: "下载日线行情",
                            iconCls: "yqwin-btn-icon fa fa-line-chart",
                            onClick: function () {
                                main_fun.load_svr_daily();
                            }
                        }, {
                            id: "load_upp_state",
                            text: "更新股票状态",
                            iconCls: "yqwin-btn-icon fa fa-star-o",
                            onClick: function () {
                                main_fun.load_upp_state();
                            }
                        }, {
                            id: "svr_test",
                            text: "测试",
                            iconCls: "yqwin-btn-icon fa fa-text-width",
                            onClick: function () {
                                test();
                            }
                        }
                    ],
                    ddSeeks: ["star"],    //下拉查询字段，字段名称
                    beforeBindingTitle: function (titles, callback) {
                        titles.splice(3, 0, {
                            is_func: true,
                            name: "k_line",
                            ui_title: '<span class="fa fa-line-chart"></span>',
                            ui_width: 30,
                            align: "center",
                            ui_visible: true,
                            formatter: function (value, row, index) {
                                return '<a href="javascript:void(0);" onclick="main_fun.show_k_line(\'' + row.ts_code + '\');"><span class="fa fa-line-chart"></span></a>';
                            }
                        });

                        console.log("beforeBindingTitle", titles);

                        callback(titles);
                    }, //标题绑定前
                    afterBindingTitle: function () {
                        var $btns = $("#" + main_fun.id).yqDataGrid("getBtns", "txt_ent_date");
                        main_fun.$txt_ent_date = $btns[0];
                        page_fun.init();
                    },
                    onBeforeShowEdit: function (titles, action, row, callback) {      //add/upp/move 编辑执行前
                        //console.log("onBeforeShowEdit", action, row);
                        if (action == "add") {
                            row["time"] = new Date();
                        }
                        callback(true, titles, row);      //true 执行编辑
                    },
                });

                $("#" + this.id).yqDataGrid(opt);
            },
            options: function () {
                return $("#" + this.id).yqDataGrid("options");
            },
            get_selected: function () {
                return $("#" + this.id).yqDataGrid("getSelected");
            },
            load_svr_data: function () {
                this.loading();
                var formData = new FormData();
                formData.append("action", "load_data");
                this.ajax_post(formData, function (res) {
                    if (res.error == 0) {
                        $.yqMessager("success", "数据加载成功。");
                        main_fun.re_seek();
                    } else {
                        $.yqAlert("error", res.info);
                    }

                    main_fun.loaded();
                });
            },
            load_svr_daily: function () {
                var $dd = $('<div id="div_show"></div>');
                var $pb = $('<div style="margin-top:8px;"></div>');
                var run_cancle = false;
                $dd.dialog({
                    closable: false,
                    title: "日线行情加载",
                    width: 415,
                    height: 127,
                    closed: false,
                    cache: false,
                    modal: true,
                    onOpen: function () {
                        var $this = $(this);
                        var $body = $this.dialog("body");
                        $body.append($pb);
                        $pb.progressbar({
                            text: "加载中[{value}%]...",
                            width: 400,
                            height: 30,
                            value: 0,
                            striped: true,
                            animate: true
                        });

                        var $btn_cancle = $('<a href="javascript:void(0);" style="margin: 10px 0 0 160px;">取消</a>');
                        $body.append($btn_cancle);
                        $btn_cancle.linkbutton({
                            iconCls: 'icon-search',
                            onClick: function () {
                                run_cancle = true;
                            }
                        });
                    },
                });

                var from_date;
                var end_date = this.$txt_ent_date.textbox("getValue");
                if (end_date == "") {
                    from_date = new Date();
                    from_date.setMonth(from_date.getMonth() - 6);
                } else {
                    from_date = stock_pub.parseYYYYMMDD(end_date);
                }
                from_date.setDate(from_date.getDate() + 1);
                end_date = new Date();

                from_date = stock_pub.formatYYYYMMDD(from_date);
                end_date = stock_pub.formatYYYYMMDD(end_date);

                console.log(from_date, end_date);

                this.loading();
                var formData = new FormData();
                formData.append("action", "load_cal");
                formData.append("start_date", from_date);
                formData.append("end_date", end_date);

                this.ajax_post(formData, function (res) {
                    console.log(res);
                    if (res.error == 0) {
                        load_daily(res.data, 0);
                    } else {
                        $.yqAlert("error", res.info + "[" + res.error + "]", "info", function () {
                            $dd.dialog("close");
                            main_fun.loaded();
                        });
                    }
                });

                function load_daily(cal_date, index) {
                    if (run_cancle || index >= cal_date.length) {
                        $pb.progressbar("setValue", 100);
                        $dd.dialog("close");

                        main_fun.loaded();
                        main_fun.clear();
                        main_fun.load_data();
                        return;
                    }

                    $pb.progressbar("setValue", (index * 100 / cal_date.length).toFixed(2));

                    formData = new FormData();
                    formData.append("action", "load_daily");
                    formData.append("trade_date", cal_date[index].cal_date);
                    main_fun.ajax_post(formData, function (res2) {
                        console.log(res2);
                        if (res2.error == 0) {
                            load_daily(cal_date, index + 1);
                        } else {
                            $.yqAlert("error", res2.info + "[" + res2.error + "]", "info", function () {
                                $dd.dialog("close");
                                main_fun.loaded();
                            });
                        }
                    });
                }
            },
            loading: function () {
                $("#" + this.id).yqDataGrid("loading");
                $("#" + this.id).yqDataGrid("isEdit", false);
                $("#" + this.id).yqDataGrid("btnDisable", "load_svr_data");
                $("#" + this.id).yqDataGrid("btnDisable", "load_svr_daily");
            },
            loaded: function () {
                $("#" + this.id).yqDataGrid("isEdit", true);
                $("#" + this.id).yqDataGrid("btnEnable", "load_svr_data");
                $("#" + this.id).yqDataGrid("btnEnable", "load_svr_daily");
                $("#" + this.id).yqDataGrid("loaded");
            },
            load_data: function () {
                main_fun.loading();

                var formData = new FormData();
                formData.append("action", "load_status");
                this.ajax_post(formData, function (res) {
                    if (res.error == 0 && res.rows.length > 0) {
                        main_fun.$txt_ent_date.textbox("setValue", res.rows[0].end_date);
                    }
                    main_fun.loaded();

                    $("#" + main_fun.id).yqDataGrid("loadData")
                });
            },
            clear: function () {
                $("#" + this.id).yqDataGrid("clear");
            },
            //更新股票状态 
            load_upp_state: function () {
                const tag = stock_pub.formatYYYYMMDD(new Date());

                var $dd = $('<div id="div_show"></div>');
                var $pb = $('<div style="margin-top:4px;"></div>');
                var $info = $('<div style="margin:8px 20px 0 0;text-align:right;">已完成0条/共0条</div>');
                var run_cancle = false;
                $dd.dialog({
                    closable: false,
                    title: "更新股票状态",
                    width: 415,
                    height: 152,
                    closed: false,
                    cache: false,
                    modal: true,
                    onOpen: function () {
                        var $this = $(this);
                        var $body = $this.dialog("body");
                        $body.append($pb);
                        $body.append($info);
                        $pb.progressbar({
                            text: "已完成[{value}%]...",
                            width: 400,
                            height: 30,
                            value: 0,
                            striped: true,
                            animate: true
                        });

                        var $btn_cancle = $('<a href="javascript:void(0);" style="margin: 10px 0 0 160px;">取消</a>');
                        $body.append($btn_cancle);
                        $btn_cancle.linkbutton({
                            iconCls: 'icon-search',
                            onClick: function () {
                                run_cancle = true;
                            }
                        });
                    },
                });

                _is_load = false;
                _pageNo = 1;
                _pageCount = 0;
                _recordCount = 0;

                tb_paging(10, 1);
                function tb_paging(pagesize, pageno) {
                    if (run_cancle) {
                        $pb.progressbar("setValue", 100);
                        $dd.dialog("close");
                        $.yqMessager("error", "更新股票状态取消。");
                        return;
                    }

                    var formData = new FormData();
                    formData.append("action", "paging");
                    formData.append("where", "(tag is null or tag!='" + tag + "')");
                    formData.append("order", "ts_code");
                    formData.append("pageSize", pagesize);
                    formData.append("pageNo", pageno);

                    main_fun.ajax_post(formData, function (res) {
                        if (res.error == 0) {

                            if (!_is_load) {
                                _is_load = true;
                                _pageNo = 1;
                                _pageCount = res.pageCount;
                                _recordCount = res.recordCount;
                                $pb.progressbar("setValue", 0);
                            } else {
                                _pageNo++;
                            }

                            $pb.progressbar("setValue", (_pageNo * 100 / _pageCount).toFixed(2));
                            $info.html("已完成" + parseInt(res.pageSize) * parseInt(_pageNo - 1) + "条/共" + _recordCount + "条");

                            tb_upp(res, 0);
                        } else {
                            $pb.progressbar("setValue", 100);
                            $dd.dialog("close");
                            $.yqMessager("error", res.info + "[" + res.error + "]");
                        }

                        console.log(res);
                    })
                }

                function tb_upp(main_res, index) {
                    if (run_cancle) {
                        $pb.progressbar("setValue", 100);
                        $dd.dialog("close");
                        $.yqMessager("error", "更新股票状态取消。");
                        return;
                    }

                    if (main_res.rows.length == 0) {
                        $pb.progressbar("setValue", 100);
                        $dd.dialog("close");
                        $.yqMessager("success", "更新股票状态完成。");

                        return;
                    }

                    if (index >= main_res.rows.length) {
                        if (main_res.pageCount <= 1) {
                            $pb.progressbar("setValue", 100);
                            $dd.dialog("close");
                            $.yqMessager("success", "更新股票状态完成。");
                            return;
                        }

                        tb_paging(main_res.pageSize, 1);
                        return;
                    }

                    var row = main_res.rows[index];
                    main_fun.uppStock(tag, row, function (res) {
                        console.log(res);
                        if (res.error == 0) {
                            $info.html("已完成" + (parseInt(main_res.pageSize) * parseInt(_pageNo - 1) + parseInt(index + 1)) + "条/共" + _recordCount + "条");
                            tb_upp(main_res, index + 1);
                        } else {
                            $pb.progressbar("setValue", 100);
                            $dd.dialog("close");
                            $.yqMessager("error", res.info + "[" + res.error + "]");
                        }
                    });
                }
            },

            uppStock: async function (tag, row, callback) {
                main_fun.fetchStockData(row["ts_code"], function (response) {
                    svr_response = response;
                    // 严格验证并转换数据格式
                    const ohlc = [];
                    // 转换K线数据
                    svr_response.rows.forEach((k, index) => {
                        if (!k.trade_date || !k.open || !k.high || !k.low || !k.close) return;

                        // 修正日期转换逻辑，确保日期正确
                        const year = parseInt(k.trade_date.slice(0, 4));
                        const month = parseInt(k.trade_date.slice(4, 6)) - 1;
                        const day = parseInt(k.trade_date.slice(6, 8));
                        if (isNaN(year) || isNaN(month) || isNaN(day)) return;
                        const date = new Date(Date.UTC(year, month, day));
                        const timestamp = date.getTime();
                        if (isNaN(timestamp)) return;

                        svr_response.rows[index].date = timestamp;

                        ohlc.push([
                            timestamp,
                            parseFloat(k.open),
                            parseFloat(k.high),
                            parseFloat(k.low),
                            parseFloat(k.close)
                        ]);
                    });

                    //10个交易日涨停、跌停次数计算
                    var limitStatus_1 = 0;
                    var limitStatus_2 = 0;
                    var crossType = 0;
                    if (ohlc.length > 10) {
                        for (var i = 10; i > 0; i--) {
                            status = checkLimitStatus(ohlc[ohlc.length - i - 1][4], ohlc[ohlc.length - i][4]);
                            if (status == 1) {
                                limitStatus_1++;
                            } else if (status == -1) {
                                limitStatus_2++;
                            }
                        }
                    }

                    const ma10 = calculate.MA(ohlc, 10);
                    const ma20 = calculate.MA(ohlc, 20);
                    crossType = checkCrossType(ma10, ma20);

                    // 20日移动平均线
                    const boll = calculate.BOLL(ohlc, 20, 2);
                    var price_state = 1;  // 1:下线下 2:中线下 3:中线上 4:上线上
                    try {
                        if (ohlc[ohlc.length - 1][4] < boll[boll.length - 1][3]) {
                            price_state = 1;
                        } else if (ohlc[ohlc.length - 1][4] >= boll[boll.length - 1][3] && ohlc[ohlc.length - 1][4] < boll[boll.length - 1][1]) {
                            price_state = 2;
                        } else if (ohlc[ohlc.length - 1][4] >= boll[boll.length - 1][1] && ohlc[ohlc.length - 1][4] < boll[boll.length - 1][2]) {
                            price_state = 3;
                        } else if (ohlc[ohlc.length - 1][4] >= boll[boll.length - 1][2]) {
                            price_state = 4;
                        }
                    } catch (e) { }

                    row["limit_status_1"] = limitStatus_1;
                    row["limit_status_2"] = limitStatus_2;
                    row["cross_type"] = crossType;
                    row["price_state"] = price_state;
                    row["tag"] = tag;

                    var formData = new FormData();
                    formData.append("action", "action");
                    formData.append("json", JSON.stringify([{
                        "action": "upp",
                        "row": row
                    }
                    ]));
                    main_fun.ajax_post(formData, function (res) {
                        callback(res);
                    })
                });
            },

            ajax_post: function (formData, callback) {
                var o = this.options();
                $.ajax({
                    type: 'POST',
                    url: o.ajaxUrl,
                    data: formData,
                    processData: false,
                    contentType: false,
                    dataType: "json",
                    success: function (res) {
                        if ($.yqDataErrCheck(res)) {
                            callback(res);
                        }
                    },
                    error: function (XMLHttpRequest, textStatus, errorThrown) {
                        console.log(XMLHttpRequest.responseText);
                        callback({
                            error: 99,
                            info: XMLHttpRequest.responseText
                        });
                    }
                });
            },
            //获取本地日线信息
            fetchStockData: async function (ts_code, callback) {
                $.ajax({
                    type: 'GET',
                    url: `/api/stock/stock_list.json?action=stock_daily&ts_code=${ts_code}`,
                    processData: false,
                    contentType: false,
                    dataType: "json",
                    success: function (res) {
                        if ($.yqDataErrCheck(res)) {
                            callback(res);
                        }
                    },
                    error: function (XMLHttpRequest, textStatus, errorThrown) {
                        console.log(XMLHttpRequest.responseText);
                        callback({ "error": 99, "info": errorThrown });
                    }
                });
            },

            re_seek: function () {
                $("#" + this.id).yqDataGrid("reSeek");
            },
            resize: function (para) {
                $("#" + this.id).yqDataGrid('resize', para);
            },
            show_k_line: function (ts_code) {
                var url = "/adp/stock/stock_view.html?ts_code=" + ts_code;
                //window.open(url, "_blank");
                var prevent_e = $.getPreventDefault($("#" + this.id));
                var mdiinfo = $.yqGetWindParams();

                var w_;
                var h_;
                if (window.top == window.self) {
                    w_ = $(document).width() - 4;
                    h_ = $(document).height() - 4;
                }
                else {
                    w_ = $(window.top.document).width() - 4;
                    h_ = $(window.top.document).height() - 4;
                }

                this.body_style = $("body").css("overflow");
                $("body").css("overflow", "hidden");
                var opt = {
                    prevent_e: prevent_e,
                    mdiinfo: mdiinfo,
                    title: "k线图",
                    width: w_,
                    height: h_,
                    buttons: [],
                    closeBtn: false,
                    showTitle: false,
                    horizontalAlign: "center",
                    verticalAlign: "center",
                    iframe: true,
                    iframeUrl: url,
                    onOpen: function ($body) {
                    },
                    onClose: function () {
                        $("body").css("overflow", main_fun.body_style);
                        delete main_fun.body_style;
                    }
                };
                if (window.top == window.self) {
                    $.yqDialog(opt);
                }
                else {
                    window.top.$.yqDialog(opt);
                }
            }
        }

        function test() {
            // var deltas = [0.1, 1, 2, 10, 100, 1000];
            // for (i = 1; i < deltas.length; i++) {
            //     angle = calculate.price_angles(deltas, i)
            //     console.log("当 delta = " + deltas[i] + " 时，角度 = " + angle + " 度")
            // }
            // return;
            var row = main_fun.get_selected();
            if (row === null) {
                $.yqMessager("error", "请选择股票");
                return;
            }
            main_fun.fetchStockData(row["ts_code"], function (response) {
                svr_response = response;
                // 严格验证并转换数据格式
                const ohlc = [];
                // 转换K线数据
                svr_response.rows.forEach((k, index) => {
                    if (!k.trade_date || !k.open || !k.high || !k.low || !k.close) return;

                    // 修正日期转换逻辑，确保日期正确
                    const year = parseInt(k.trade_date.slice(0, 4));
                    const month = parseInt(k.trade_date.slice(4, 6)) - 1;
                    const day = parseInt(k.trade_date.slice(6, 8));
                    if (isNaN(year) || isNaN(month) || isNaN(day)) return;
                    const date = new Date(Date.UTC(year, month, day));
                    const timestamp = date.getTime();
                    if (isNaN(timestamp)) return;

                    svr_response.rows[index].date = timestamp;

                    ohlc.push([
                        timestamp,
                        parseFloat(k.open),
                        parseFloat(k.high),
                        parseFloat(k.low),
                        parseFloat(k.close),
                        parseFloat(k.vol),
                    ]);
                });

                console.log(svr_response);
                console.log(ohlc);

                //10个交易日涨停、跌停次数计算
                var limitStatus_1 = 0;
                var limitStatus_2 = 0;
                var crossType = 0;
                if (ohlc.length > 10) {
                    for (var i = 10; i > 0; i--) {
                        status = checkLimitStatus(ohlc[ohlc.length - i - 1][4], ohlc[ohlc.length - i][4]);
                        if (status == 1) {
                            limitStatus_1++;
                        } else if (status == -1) {
                            limitStatus_2++;
                        }
                    }
                }
                console.log("涨停次数：", limitStatus_1);
                console.log("跌停次数：", limitStatus_2);
                // 计算MACD指标
                const ma10 = calculate.MA(ohlc, 10);
                const ma20 = calculate.MA(ohlc, 20);
                crossType = checkCrossType(ma10, ma20);
                console.log("crossType：", crossType);

                // 20日移动平均线
                const boll = calculate.BOLL(ohlc, 20, 2);
                console.log("upp", boll[boll.length - 1][2]);
                console.log("m20", boll[boll.length - 1][1]);
                console.log("lower", boll[boll.length - 1][3]);
                console.log("close：", ohlc[ohlc.length - 1][4]);

                var price_state = 1;  // 1:下线下 2:中线下 3:中线上 4:上线上
                if (ohlc[ohlc.length - 1][4] < boll[boll.length - 1][3]) {
                    price_state = 1;
                } else if (ohlc[ohlc.length - 1][4] >= boll[boll.length - 1][3] && ohlc[ohlc.length - 1][4] < boll[boll.length - 1][1]) {
                    price_state = 2;
                } else if (ohlc[ohlc.length - 1][4] >= boll[boll.length - 1][1] && ohlc[ohlc.length - 1][4] < boll[boll.length - 1][2]) {
                    price_state = 3;
                } else if (ohlc[ohlc.length - 1][4] >= boll[boll.length - 1][2]) {
                    price_state = 4;
                }
                console.log("price_state：", price_state);

                row["limit_status_1"] = limitStatus_1;
                row["limit_status_2"] = limitStatus_2;

                row["cross_type"] = crossType;

                var volumeRatio = calculateVolumeRatio(ohlc, 5)
                console.log("volumeRatio", volumeRatio);

                console.log(row);
            });
        }

        //量比的计算公式
        function calculateVolumeRatio(ohlc, period = 5) {
            const volumeRatios = [];
            for (let i = period; i < ohlc.length; i++) {
                let totalVolume = 0;
                for (let j = i - period; j < i; j++) {
                    totalVolume += ohlc[j][5];
                }
                const averageVolume = totalVolume / period;
                const currentVolume = ohlc[i][5];
                const volumeRatio = currentVolume / averageVolume;
                volumeRatios.push([ohlc[i][0], volumeRatio, stock_pub.formatYYYYMMDD(new Date(ohlc[i][0]))]);
            }
            return volumeRatios;
        }

        // 判断涨跌停的函数
        // 1:涨停 0:正常 -1:跌停
        function checkLimitStatus(prevClose, currentClose) {
            // 假设涨跌停幅度为 10%，可根据实际情况调整
            const limitUp = prevClose * 1.1;
            const limitDown = prevClose * 0.9;

            if (currentClose >= limitUp) {
                return 1;
            } else if (currentClose <= limitDown) {
                return -1;
            } else {
                return 0;
            }
        }

        // 检查交叉类型的函数
        // 2:死叉 1:全叉 0:无交叉
        function checkCrossType(short_ma, long_ma) {
            if (long_ma.length < 2) {
                return 0;
            }

            // 检查是否为死叉
            if (short_ma[short_ma.length - 1][1] < long_ma[long_ma.length - 1][1] && short_ma[short_ma.length - 2][1] > long_ma[long_ma.length - 2][1]) {
                return 2;
            } else if (short_ma[short_ma.length - 1][1] > long_ma[long_ma.length - 1][1] && short_ma[short_ma.length - 2][1] < long_ma[long_ma.length - 2][1]) {
                return 1;
            } else {
                return 0;
            }
        }

    </script>
</body>

</html>